﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Sttplay.Transmission.TCP
{
    public class TCPServer
    {
        private Socket server;
        private List<Socket> sockets = new List<Socket>();
        private Dictionary<Socket, TCPClient> allClient = new Dictionary<Socket, TCPClient>();

        public event Action<string> onErrorLogEvent;
        public event Action<TCPClient> onClientConnectEvent;
        public event Action<TCPClient> onClientDisconnectEvent;
        public event Action<TCPClient, byte[]> onClientRecvEvent;
        public int ConnectCount { get { return allClient.Count; } }
        public Dictionary<Socket, TCPClient> AllClient { get { return allClient; } }
        private bool isPacket;
        public TCPServer(bool isPacket = false)
        {
            this.isPacket = isPacket;
        }

        public bool Initialize(IPAddress ip, int port)
        {
            Terminate();
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                server.Bind(new IPEndPoint(ip, port));
                server.Listen(0);
                sockets.Add(server);
            }
            catch (Exception ex)
            {
                OnErrorLog(ex.ToString());
                return false;
            }
            return true;
        }

        public void DisconnectClient(TCPClient proxy)
        {
            sockets.Remove(proxy.Socket);
            AllClient.Remove(proxy.Socket);
            proxy.Disconnect();
        }

        public void Terminate()
        {
            foreach (var item in allClient)
                item.Value.Disconnect();

            if (server != null)
            {
                server.Close();
                server.Dispose();
            }
            sockets.Clear();
            allClient.Clear();
            server = null;
        }

        public void Update(int timeout = 0)
        {
            System.Threading.Thread.Sleep(timeout);
            if (server == null)
                return;

            foreach (var item in allClient)
            {
                item.Value.Update();
            }
            while (sockets.Count > 0)
            {
                List<Socket> temp = new List<Socket>();
                foreach (var item in allClient)
                {
                    if (!item.Value.Removed)
                        temp.Add(item.Key);
                }
                temp.Add(server);
                Socket.Select(temp, null, null, 0);
                if (temp.Count <= 0)
                    break;
                for (int i = 0; i < temp.Count; i++)
                {
                    if (temp[i] == server)
                    {
                        Socket client = server.Accept();
                        TCPClient proxy = new TCPClient(client, OnClientRecv, OnErrorLog, isPacket);
                        sockets.Add(client);
                        allClient.Add(client, proxy);
                        OnClientConnect(proxy);
                    }
                    else
                    {
                        TCPClient proxy = allClient[temp[i]];
                        int len = 0;
                        SocketError error = proxy.Receive(ref len);

                        if (len <= 0 || error != SocketError.Success)
                        {
                            if (error != SocketError.Success)
                                OnErrorLog("recv error:" + error);
                            OnClientDisconnect(proxy);
                            proxy.Disconnect();
                        }
                    }
                }
                for (int i = 1; i < sockets.Count; i++)
                {
                    if (allClient[sockets[i]].Removed)
                    {
                        allClient.Remove(sockets[i]);
                        sockets.RemoveAt(i);
                        i--;
                    }
                }
            }
        }

        public void Broadcast(byte[] buffer, int offset, int size)
        {
            foreach (var item in allClient)
                item.Value.Send(buffer, offset, size);
        }

        private void OnClientConnect(TCPClient proxy)
        {
            if (onClientConnectEvent != null)
                onClientConnectEvent(proxy);
        }

        private void OnClientDisconnect(TCPClient proxy)
        {
            if (onClientDisconnectEvent != null)
                onClientDisconnectEvent(proxy);
        }

        private void OnClientRecv(TCPClient proxy, byte[] data)
        {
            if (onClientRecvEvent != null)
                onClientRecvEvent(proxy, data);
        }

        private void OnErrorLog(string error)
        {
            if (onErrorLogEvent != null)
                onErrorLogEvent(error);
        }
    }
}
